package cn.easyutil.project.base.jdbc;

import java.math.BigDecimal;
import java.util.regex.Pattern;

/**
 * @author StevenChen
 * @since 2020/6/8
 */
public enum MySQLJDBCTypeEnum {

    // 默认无
    NONE("none") {
        @Override
        public boolean valueEnable(Object value) {
            return false;
        }
    },

    // 用于boolean或是 只有0和1的值类型
    BIT("bit") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            return new NumberValueEnable(Boolean.TYPE, "0 or 1", Boolean.class, "0", "1").valueEnable(value);
        }
    },

    // 用于范围较小的int数值
    BYTE("tinyint") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            return new NumberValueEnable(Byte.TYPE, "(-128~127) or (0~255)", Byte.class, "-128", "255").valueEnable(value);
        }
    },

    // 用于范围适中的int数值
    SHORT("smallint") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            return new NumberValueEnable(Short.TYPE, "(-32768~32767) or (0~65535)", Short.class, "-32768", "65535").valueEnable(value);
        }
    },

    INTEGER("int") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            return new NumberValueEnable(Integer.TYPE, "(-2147483648~2147483647) or (0~4294967295)", Integer.class, "-2147483648", "4294967295").valueEnable(value);
        }
    },

    LONG("bigint") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            return new NumberValueEnable(Long.TYPE, "(-9223372036854775808~9223372036854775807) or (0~18446744073709551615)", Long.class, "-9223372036854775808", "18446744073709551615").valueEnable(value);
        }
    },

    FLOAT("float") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            final String rangeStr = Float.MIN_VALUE + "~" + Float.MAX_VALUE;
            return new NumberValueEnable(Float.TYPE, rangeStr, Float.class, Float.MIN_VALUE + "", Float.MAX_VALUE + "").valueEnable(value);
        }
    },

    DOUBLE("double") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            final String rangeStr = Double.MIN_VALUE + "~" + Double.MAX_VALUE;
            return new NumberValueEnable(Double.TYPE, rangeStr, Double.class, Double.MIN_VALUE + "", Double.MAX_VALUE + "").valueEnable(value);
        }
    },

    DECIMAL("decimal") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            if (value == null) {
                return false;
            }
            if (NUMBER_PATTERN.matcher(value.toString()).matches()) {
                return true;
            }
            final String message = String.format("value type mismatch, expect number value, actual value %s", value).intern();
            throw new TypeMismatchException(message);
        }
    },

    VARCHAR("VARCHAR") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            if (value == null) {
                return false;
            }
            if (value instanceof String) {
                return ((String) value).length() <= 255;
            }
            final String message = String.format("value type mismatch, expect String value, actual value %s", value).intern();
            throw new TypeMismatchException(message);
        }
    },

    TEXT("text") {
        @Override
        public boolean valueEnable(Object value) throws TypeMismatchException {
            if (value == null) {
                return false;
            }
            if (value instanceof String) {
                return true;
            }
            final String message = String.format("value type mismatch, expect String value, actual value %s", value).intern();
            throw new TypeMismatchException(message);
        }
    };

    private static final Pattern NUMBER_PATTERN = Pattern.compile("^-?\\d+(.\\d+)?$");

    private final String jdbcType;

    MySQLJDBCTypeEnum(String jdbcType) {
        this.jdbcType = jdbcType;
    }

    public String enumType2JdbcType() {
        return this.jdbcType;
    }

    public abstract boolean valueEnable(Object value) throws TypeMismatchException;

    private static class NumberValueEnable {

        private final Class<?> type;
        private final String rangeStr;
        private final Class<?> classType;
        private final BigDecimal minValue;
        private final BigDecimal maxValue;

        public NumberValueEnable(Class<?> type, String rangeStr, Class<?> classType, String minValue, String maxValue) {
            this.type = type;
            this.rangeStr = rangeStr;
            this.classType = classType;
            this.minValue = new BigDecimal(minValue);
            this.maxValue = new BigDecimal(maxValue);
        }

        boolean valueEnable(Object value) throws TypeMismatchException {

            if (value == null) {
                return false;
            }

            if (classType.equals(value.getClass())) {
                return true;
            }

            if (type != null && type.equals(value.getClass())) {
                return true;
            }

            if (!NUMBER_PATTERN.matcher(value.toString()).matches()) {
                return false;
            }

            BigDecimal numberValue = new BigDecimal(value.toString());
            int compareMin = numberValue.compareTo(minValue);
            int compareMax = numberValue.compareTo(maxValue);
            if (compareMin >= 0 && compareMax <= 0) {
                return true;
            }

            final String message = String.format("value type mismatch, expect value %s, actual value %s", rangeStr, value).intern();
            throw new TypeMismatchException(message);
        }
    }
}
